package daybook.util;

import org.json.JSONArray;

import java.util.*;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public final class Utils {
    private Utils() {}

    private static final String[] SPACE = new String[] {"", " ", "  ", "   ", "    ", "     ", "      ", "       ", "        ", "         ", "          "};

    public static String spaces(int n) {
        if (n < 0) {
            throw new IllegalArgumentException();
        } else if (n < SPACE.length) {
            return SPACE[n];
        } else {
            final char[] spaces = new char[n];
            Arrays.fill(spaces, ' ');
            return new String(spaces);
        }
    }

    public static <K, V> void putUniquely(Map<K, V> map, K key, V value) {
        final V oldValue = map.get(key);
        if (oldValue != null) {
            throw new IllegalArgumentException("duplicated key: " + key + ", old value: " + oldValue + ", new value " + value);
        } else {
            map.put(key, value);
        }
    }

    public static <K, V> void addToMultiMap(Map<K, List<V>> map, K key, V value) {
        final List<V> values = map.computeIfAbsent(key, k -> new ArrayList<>());
        values.add(value);
    }

    public static <K, V> Map<K, List<V>> minifyMultiMap(Map<K, List<V>> map) {
        for (K key: map.keySet()) {
            if (map.get(key).isEmpty()) {
                map.remove(key);
            }
        }
        return map;
    }

    public static <K, V> boolean isEmptyMultiMap(Map<K, List<V>> map) {
        return map.isEmpty() || map.values().stream().allMatch(List::isEmpty);
    }

    public static <T extends Comparable<? super T>> T min(T val1, T val2) {
        return  (val1.compareTo(val2) <= 0) ? val1 : val2;
    }

    public static <T extends Comparable<? super T>> T max(T val1, T val2) {
        return  (val1.compareTo(val2) >= 0) ? val1 : val2;
    }

    public static <K, V, M extends Map<K, V>> M merge(M map1, M map2, BinaryOperator<V> combiner) {
        if (map1.size() < map2.size()) {
            final M tmp = map2;
            map2 = map1;
            map1 = tmp;
        }

        mergeInto(map1, map2, combiner);

        return map1;
    }

    public static <K, V> void mergeInto(Map<K, V> map1, Map<K, V> map2, BinaryOperator<V> combiner) {
        for (Map.Entry<K, V> entry: map2.entrySet()) {
            map1.merge(entry.getKey(), entry.getValue(), combiner);
        }
    }

    public static boolean isBlank(String str) {
        return str == null || str.trim().isEmpty();
    }

    private static class JSONArrayIterator<T> implements Iterator<T> {
        private final JSONArray array;
        private int i = 0;
        private final BiFunction<JSONArray, Integer, T> getter;

        private JSONArrayIterator(JSONArray array, BiFunction<JSONArray, Integer, T> getter) {
            this.array = Objects.requireNonNull(array);
            this.getter = Objects.requireNonNull(getter);
        }

        @Override
        public boolean hasNext() {
            return i < array.length();
        }

        @Override
        public T next() {
            if (i >= array.length()) {
                throw new NoSuchElementException();
            }
            return getter.apply(array, i++);
        }
    }

    public static <T> Iterator<T> typeSafeIterator(JSONArray array, BiFunction<JSONArray, Integer, T> getter) {
        return new JSONArrayIterator<>(array, getter);
    }

    public static <T> Stream<T> toStream(JSONArray array, BiFunction<JSONArray, Integer, T> getter, boolean parallel) {
        return StreamSupport.stream(Spliterators.spliterator(typeSafeIterator(array, getter), array.length(), Spliterator.ORDERED), parallel);
    }

    public static <T> Stream<T> toStream(JSONArray array, BiFunction<JSONArray, Integer, T> getter) {
        return toStream(array, getter, false);
    }

    // result >= -1 and result < index
    // 找到list中index位置左边第一个值不是list.get(index)的索引
    public static <T> int shiftLeft(List<T> list, int index, Comparator<T> c) {
        final T elem = list.get(index);
        do {
            index--;
        } while (index >= 0 && c.compare(list.get(index), elem) == 0);

        return index;
    }

    // result > index and result <= list.size()
    // 找到list中index位置左边第一个值不是list.get(index)的索引
    public static <T> int shiftRight(List<T> list, int index, Comparator<T> c) {
        final T elem = list.get(index);
        do {
            index++;
        } while (index < list.size() && c.compare(list.get(index), elem) == 0);

        return index;
    }

    // <op> = '>=' if inclusive is true or '>' if inclusive is false
    // result >= -1 && result < list.size()
    // if result == -1, then no c.compare(key, list.get(0)) <op> 0 is false
    // if result != -1, then c.compare(key, list.get(result)) <op> 0
    // for all i such that c.compare(key, list.get(i)) <op> 0, it has that result >= i
    public static <T> int binaryRightIndex(List<T> list, T key, Comparator<T> c, boolean inclusive) {
        int index = Collections.binarySearch(list, key, c);

        if (index == list.size()) {
            return index - 1;
        } else if (index >= 0) {
            if (inclusive) {
                index = shiftRight(list, index, c) - 1;
            } else {
                index = shiftLeft(list, index, c);
            }
        } else { // index < 0
            index = -(index + 1) - 1;
        }

        return index;
    }

    // <op> = '<=' if inclusive is true or '<' if inclusive is false
    // result >= 0 && result <= list.size()
    // if result == list.size(), then no c.compare(key, list.get(0)) <op> 0 is false
    // if result != list.size(), then c.compare(key, list.get(result)) <op> 0
    // for all i such that c.compare(key, list.get(i)) <op> 0, it has that result <= i
    public static <T> int binaryLeftIndex(List<T> list, T key, Comparator<T> c, boolean inclusive) {
        int index = Collections.binarySearch(list, key, c);

        if (index == list.size()) {
            return index;
        } else if (index >= 0) {
            if (inclusive) {
                index = shiftLeft(list, index, c) + 1;
            } else {
                index = shiftRight(list, index, c);
            }
        } else { // index < 0
            index = -(index + 1);
        }

        return index;
    }

    // list是按c升序排序的列表
    // 返回list中key左侧的子表, 如果inclusive == true, 则包含key, 反之则不包含key
    public static <T> List<T> binaryLeftSubList(List<T> list, T key, Comparator<T> c, boolean inclusive) {
//        int index = Collections.binarySearch(list, key, c);
//        if (index >= 0) {
//            if (inclusive) {
//                index = shiftRight(list, index, c);
//            } else {
//                index = shiftLeft(list, index, c) + 1;
//            }
//        }
//        if (index == list.size()) {
//            return list;
//        } else if (index == -1 || index == 0) {
//            return Collections.emptyList();
//        } else if (index < -1) {
//            index = -(index + 1);
//        }
        final int index = binaryRightIndex(list, key, c, inclusive) + 1;

        return list.subList(0, index);
    }

    // list是按c升序排序的列表
    // 返回list中key右侧的子表, 如果inclusive == true, 则包含key, 反之则不包含key
    public static <T> List<T> binaryRightSubList(List<T> list, T key, Comparator<T> c, boolean inclusive) {
//        int index = Collections.binarySearch(list, key, c);
//        if (index >= 0) {
//            if (inclusive) {
//                index = shiftLeft(list, index, c) + 1;
//            } else {
//                index = shiftRight(list, index, c);
//            }
//        }
//        if (index == list.size()) {
//            return Collections.emptyList();
//        } else if (index == -1 || index == 0) {
//            return list;
//        } else if (index < -1) {
//            index = -(index + 1);
//        }
        final int index = binaryLeftIndex(list, key, c, inclusive);

        return list.subList(index, list.size());
    }

    // list是按c升序排序的列表, leftKey <= rightKey
    // 返回list中leftKey到rightKey之间的子表
    // 如果leftInclusive == true, 则包含leftKey, 反之则不包含leftKey
    // 如果rightInclusive == true, 则包含rightKey, 反之则不包含rightKey
    public static <T> List<T> binarySubList(List<T> list, T leftKey, boolean leftInclusive, T rightKey, boolean rightInclusive, Comparator<T> c) {
        final int cmp = c.compare(leftKey, rightKey);
        if (cmp > 0) {
            throw new IllegalArgumentException("leftKey > rightKey");
        } else if (cmp == 0) {
            if (leftInclusive && rightInclusive) {
                final int index = Collections.binarySearch(list, leftKey, c);
                if (index > 0 && index != list.size()) {
                    final int leftIndex = shiftLeft(list, index, c) + 1;
                    final int rightIndex = shiftRight(list, index, c);
                    if (rightIndex == leftIndex) {
                        return Collections.singletonList(list.get(index));
                    } else {
                        return list.subList(leftIndex, rightIndex);
                    }
                } else {
                    return Collections.emptyList();
                }
            } else {
                return Collections.emptyList();
            }
        } else {
            final List<T> leftSubList = binaryLeftSubList(list, rightKey, c, rightInclusive);
            return binaryRightSubList(leftSubList, leftKey, c, leftInclusive);
        }
    }
}
